// Package safesql 提供"离线"安全拼接 SQL 占位符的功能，
// 仅允许 int/uint/float/string/time 五种不可注入类型。
package safesql

import (
	"fmt"
	"strings"
	"time"
)

// Replace 把 sql 里的 "?" 按顺序替换成 args 的安全字面量，
// 返回可直接执行的完整 SQL 字符串。
// 若出现未在白名单内的类型，会 panic。
// 如果占位符数量与参数数量不相等，则返回错误。
func Replace(sql string, args ...interface{}) (string, error) {
	placeholderCount := strings.Count(sql, "?")
	if placeholderCount != len(args) {
		return "", fmt.Errorf("safesql: 占位符数量(%d)与参数数量(%d)不匹配", placeholderCount, len(args))
	}

	for _, arg := range args {
		sql = strings.Replace(sql, "?", literal(arg), 1)
	}
	return sql, nil
}

// literal 返回 arg 的 SQL 字面量表示，并做最小转义。
func literal(arg interface{}) string {
	switch v := arg.(type) {
	case int, int8, int16, int32, int64,
		uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", v)
	case float32, float64:
		return fmt.Sprintf("%g", v)
	case string:
		return `'` + strings.ReplaceAll(v, `'`, `\'`) + `'`
	case time.Time:
		return `'` + v.Format("2006-01-02 15:04:05") + `'`
	default:
		panic(fmt.Sprintf("safesql: 不支持该类型 %T", arg))
	}
}
